package com.web.service.impl;

import com.common.dto.RankDTO;
import com.common.entity.Developer;
import com.web.helper.calculation.CaculateCommits;
import com.web.helper.calculation.CaculateEvents;
import com.web.helper.calculation.CaculateStart;
import com.web.helper.calculation.CaculateTotalStart;
import com.web.service.RankModel;
import org.springframework.stereotype.Service;

import java.util.List;

import static com.common.constant.ModelParameter.*;

/**
 * 映射函数
 */

@Service
public class RankModelImpl implements RankModel {


    //计算获取各个指标参数
    public Developer calulateParameter(Developer developer){
        String userName = developer.getUserName();
        CaculateCommits caculateCommits = new CaculateCommits();
        CaculateStart caculateStart = new CaculateStart();
        CaculateTotalStart caculateTotalStart = new CaculateTotalStart();
        CaculateEvents caculateEvents = new CaculateEvents();

        int commits = caculateCommits.process(userName);
        int start =  caculateTotalStart.process(userName);
        int totalStart = caculateStart.process(userName);
        List<Integer> list = caculateEvents.process(userName);
        int pr = list.get(0);
        int issue = list.get(1);
        int fork = list.get(2);

        developer.setCommits(commits);
        developer.setStart(start);
        developer.setTotalstart(totalStart);
        developer.setPrs(pr);
        developer.setIssues(issue);
        developer.setForks(fork);

        return developer;
    }

    public RankDTO calculateRank(Developer developer) {
        int commits = developer.getCommits();
        int prs = developer.getPrs();
        int issues = developer.getIssues();
        int repos = developer.getPublicRepos();
        int stars = developer.getStart();
        int followers = developer.getFollowers();
        int fork = developer.getForks();
        int totalStart = developer.getTotalstart();

        // Levels and Thresholds
        final String[] levels = {"S", "A+", "A", "A-", "B+", "B", "B-", "C+", "C"};
        final double[] thresholds =  {100, 87.5, 75, 62.5, 50, 37.5, 25, 12.5, 1};

        double totalWeight = COMMITS_WEIGHT + PRS_WEIGHT + ISSUES_WEIGHT + REPOS_WEIGHT
                + STARS_WEIGHT + FOLLOWERS_WEIGHT + FORKS_WEIGHT + TOTALSTARS_WEIGHT;

        // 归一化和标准化
        double rank = (
                COMMITS_WEIGHT * normalization(commits, COMMIR_MIN, COMMIT_MAX) +
                        PRS_WEIGHT * normalization(prs, PR_MIN, PR_MAX) +
                        ISSUES_WEIGHT * normalization(issues, ISSUE_MIN, ISSUE_MAX) +
                        REPOS_WEIGHT * Standardization(repos, REPO_MEAN, REPO_StdDEV) +
                        STARS_WEIGHT * Standardization(stars, STAR_MEAN, STAR_StdDEV) +
                        FOLLOWERS_WEIGHT * Standardization(followers, FOLLOWER_MEAN, FOLLOER_StdDEV) +
                        FORKS_WEIGHT * normalization(fork, FORK_MIN, FORK_MAX) +
                        TOTALSTARS_WEIGHT * normalization(totalStart, TOTALSTAR_MIN, TOTALSTAR_MAX)
        ) / totalWeight;

        // 确保 rank 在 [0, 1] 范围内
        rank = Math.max(0, Math.min(rank, 1));

        String level = levels[findLevelIndex(rank * 100, thresholds)];

        return new RankDTO(level, rank * 100);
    }

    private static int findLevelIndex(double rankPercentage, double[] thresholds) {
        for (int i = 0; i < thresholds.length; i++) {
            if (rankPercentage >= thresholds[i]) {
                return i;
            }
        }
        return thresholds.length - 1; // Return the last index if no threshold is met
    }

    private double normalization(double x, double Min, double Max) {
        if (Max == Min) {
            return 0;  // Avoid division by zero
        }
        if (x < Min) {
            return 0;  // Ensure value is within the Min-Max range
        }
        if (x > Max) {
            return 1;  // Ensure value is within the Min-Max range
        }
        return (x - Min) / (Max - Min);
    }

    private double Standardization(double x, double mean, double stdDev) {
        if (stdDev == 0) {
            return 0; // Avoid division by zero
        }
        return (x - mean) / stdDev;
    }



}

